﻿using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace Gmantis.Controls.Toolbar
{
    [TemplatePart(Name = "VerticalTemplate", Type = typeof(Grid)), TemplatePart(Name = "HorizontalTemplate", Type = typeof(Grid))]
    public class ToolbarDropDown : DropDown, IToolbarControl
    {
        // Fields
        internal Grid _elementHorizontalTemplate;
        internal Grid _elementVerticalTemplate;
        private string _state;
        public static readonly DependencyProperty ContentBackgroundProperty = DependencyProperty.Register("ContentBackground", typeof(Brush), typeof(ToolbarDropDown), null);
        internal const string HorizontalTemplateElementName = "HorizontalTemplate";
        public static readonly DependencyProperty LabelTitleProperty = DependencyProperty.Register("LabelTitle", typeof(string), typeof(ToolbarDropDown), new PropertyMetadata("", new PropertyChangedCallback(ToolbarDropDown.OnDropDownChanged)));
        public static readonly DependencyProperty LargeImageSourceProperty = DependencyProperty.Register("LargeImageSource", typeof(ImageSource), typeof(ToolbarDropDown), new PropertyMetadata(null, new PropertyChangedCallback(ToolbarDropDown.OnDropDownChanged)));
        public static readonly DependencyProperty MenuProperty = DependencyProperty.Register("Menu", typeof(ContextMenu), typeof(ToolbarDropDown), new PropertyMetadata(new PropertyChangedCallback(ToolbarDropDown.OnMenuPropertyChanged)));
        public static readonly DependencyProperty MouseOverBrushProperty = DependencyProperty.Register("MouseOverBrush", typeof(Brush), typeof(ToolbarDropDown), null);
        internal Popup popup;
        public static readonly DependencyProperty PressedBrushProperty = DependencyProperty.Register("PressedBrush", typeof(Brush), typeof(ToolbarDropDown), null);
        public static readonly DependencyProperty SmallImageSourceProperty = DependencyProperty.Register("SmallImageSource", typeof(ImageSource), typeof(ToolbarDropDown), new PropertyMetadata(null, new PropertyChangedCallback(ToolbarDropDown.OnDropDownChanged)));
        internal const string VerticalTemplateElementName = "VerticalTemplate";

        // Methods
        public ToolbarDropDown()
        {
            base.DefaultStyleKey = typeof(ToolbarDropDown);
            this.SetCustomDefaultValues();
        }

        void IToolbarControl.ChangeState(string state)
        {
            this._state = ButtonHelper.GetState(state, this.LargeImageSource, this.SmallImageSource, this.LabelTitle);
            base.InvalidateMeasure();
        }

        void IToolbarControl.FireClick()
        {
        }

        internal ToolbarControlSize GetDefaultControlSize()
        {
            switch (GetDefaultState(this.LargeImageSource, this.SmallImageSource, this.LabelTitle))
            {
                case "Large":
                    return ToolbarControlSize.Large;

                case "Small":
                    return ToolbarControlSize.Small;
            }
            return ToolbarControlSize.Medium;
        }

        private static string GetDefaultState(ImageSource largeImageSource, ImageSource smallImageSource, string labelTitle)
        {
            if (largeImageSource != null)
            {
                return "Large";
            }
            if ((smallImageSource != null) && (labelTitle == null))
            {
                return "Small";
            }
            return "Medium";
        }

        private T GetTemplateChild<T>(string childName, bool required, ref string errors) where T : class
        {
            DependencyObject templateChild = base.GetTemplateChild(childName);
            ApplyTemplateHelper.VerifyTemplateChild(typeof(T), "template part", childName, templateChild, required, ref errors);
            return (templateChild as T);
        }

        private static Storyboard GetTemplateChildResource(FrameworkElement root, string resourceName, bool required, ref string errors)
        {
            return GetTemplateChildResource<Storyboard>(root, resourceName, required, ref errors);
        }

        private static T GetTemplateChildResource<T>(FrameworkElement root, string resourceName, bool required, ref string errors) where T : class
        {
            object child = root.Resources[resourceName];
            ApplyTemplateHelper.VerifyTemplateChild(typeof(T), "resource", resourceName, child, required, ref errors);
            return (child as T);
        }

        private void InitializeHorizontalTemplatePart()
        {
        }

        private void InitializeVerticalTemplatePart()
        {
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            if (!string.IsNullOrEmpty(this._state))
            {
                VisualStateManager.GoToState(this, this._state, false);
                this._state = null;
            }
            else
            {
                VisualStateManager.GoToState(this, GetDefaultState(this.LargeImageSource, this.SmallImageSource, this.LabelTitle), false);
            }
            this.UpdateHeaderPresenter();
            return base.MeasureOverride(availableSize);
        }

        private void Menu_Closed(object sender, EventArgs e)
        {
            base.IsDropDownOpen = false;
            this.Menu.DetectBoundaries = false;
        }

        private void OnAfterApplyTemplate()
        {
            this.popup = base.GetTemplateChild("DropDownPopup") as Popup;
            if (this.popup != null)
            {
                this.popup.Opened += new EventHandler(this.popup_Opened);
            }
            this.UpdatePopupVisibility();
        }

        public override void OnApplyTemplate()
        {
            string errors = string.Empty;
            base.OnApplyTemplate();
            this._elementVerticalTemplate = this.GetTemplateChild<Grid>("VerticalTemplate", false, ref errors);
            if (this._elementVerticalTemplate != null)
            {
                this.InitializeVerticalTemplatePart();
            }
            this._elementHorizontalTemplate = this.GetTemplateChild<Grid>("HorizontalTemplate", false, ref errors);
            if (this._elementHorizontalTemplate != null)
            {
                this.InitializeHorizontalTemplatePart();
            }
            if (!string.IsNullOrEmpty(errors))
            {
                if ((base.Template != null) && !PlatformIndependent.IsInDesignMode(this))
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Template cannot be applied to ToolbarDropDown.\nDetails: {0}", new object[] { errors }));
                }
            }
            else
            {
                this.OnAfterApplyTemplate();
            }
        }

        private static void OnDropDownChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            ((FrameworkElement)obj).InvalidateMeasure();
        }

        private void OnMenuChanged(object oldValue)
        {
            ContextMenu menu = oldValue as ContextMenu;
            if (menu != null)
            {
                menu.Closed -= new EventHandler(this.Menu_Closed);
                menu.AutoClosed -= new EventHandler<EventArgs>(this.Menu_Closed);
            }
            menu = this.Menu;
            if (menu != null)
            {
                menu.Closed += new EventHandler(this.Menu_Closed);
                menu.AutoClosed += new EventHandler<EventArgs>(this.Menu_Closed);
            }
            this.UpdatePopupVisibility();
        }

        private static void OnMenuPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ToolbarDropDown down = d as ToolbarDropDown;
            ContextMenu oldValue = (ContextMenu)e.OldValue;
            down.OnMenuChanged(oldValue);
        }

        private void popup_Opened(object sender, EventArgs e)
        {
            if ((base.Content == null) && (this.Menu != null))
            {
                FrameworkElement rootVisual = Application.Current.RootVisual as FrameworkElement;
                if (rootVisual != null)
                {
                    new Size(rootVisual.ActualWidth, rootVisual.ActualHeight);
                    Point point = base.TransformToVisual(rootVisual).Transform(new Point());
                    this.Menu.DetectBoundaries = true;
                    this.Menu.Show(null, new Point(point.X, point.Y + base.ActualHeight));
                }
            }
        }

        private void SetCustomDefaultValues()
        {
            Binding binding = new Binding("Foreground")
            {
                Source = this
            };
            base.SetBinding(HeaderedContentControl.HeaderForegroundProperty, binding);
        }

        private void UpdateHeaderPresenter()
        {
            if (base._elementHeaderPresenter != null)
            {
                Grid parent = base._elementHeaderPresenter.Parent as Grid;
                if (parent != null)
                {
                    parent.Children.Remove(base._elementHeaderPresenter);
                }
                if ((this._elementVerticalTemplate != null) && (this._elementVerticalTemplate.Visibility == Visibility.Collapsed))
                {
                    if ((this._elementVerticalTemplate != null) && this._elementVerticalTemplate.Children.Contains(base._elementHeaderPresenter))
                    {
                        this._elementVerticalTemplate.Children.Remove(base._elementHeaderPresenter);
                    }
                    if ((this._elementHorizontalTemplate != null) && !this._elementHorizontalTemplate.Children.Contains(base._elementHeaderPresenter))
                    {
                        this._elementHorizontalTemplate.Children.Add(base._elementHeaderPresenter);
                    }
                }
                else
                {
                    if ((this._elementHorizontalTemplate != null) && this._elementHorizontalTemplate.Children.Contains(base._elementHeaderPresenter))
                    {
                        this._elementHorizontalTemplate.Children.Remove(base._elementHeaderPresenter);
                    }
                    if ((this._elementVerticalTemplate != null) && !this._elementVerticalTemplate.Children.Contains(base._elementHeaderPresenter))
                    {
                        this._elementVerticalTemplate.Children.Add(base._elementHeaderPresenter);
                    }
                }
            }
        }

        private void UpdatePopupVisibility()
        {
            if (this.Menu != null)
            {
                if ((this.popup != null) && (this.popup.Child != null))
                {
                    this.popup.Child.Opacity = 0.0;
                    this.popup.Width = 0.0;
                }
            }
            else if ((this.popup != null) && (this.popup.Child != null))
            {
                this.popup.Child.Opacity = 1.0;
                this.popup.ClearValue(FrameworkElement.WidthProperty);
            }
        }

        // Properties
        public Brush ContentBackground
        {
            get { return (Brush)GetValue(ContentBackgroundProperty); }
            set { SetValue(ContentBackgroundProperty, value); }
        }

        public string LabelTitle
        {
            get { return (string)GetValue(LabelTitleProperty); }
            set { SetValue(LabelTitleProperty, value); }
        }

        public ImageSource LargeImageSource
        {
            get { return (ImageSource)GetValue(LargeImageSourceProperty); }
            set { SetValue(LargeImageSourceProperty, value); }
        }

        public ContextMenu Menu
        {
            get { return (ContextMenu)GetValue(MenuProperty); }
            set { SetValue(MenuProperty, value); }
        }

        public Brush MouseOverBrush
        {
            get { return (Brush)GetValue(MouseOverBrushProperty); }
            set { SetValue(MouseOverBrushProperty, value); }
        }

        public Brush PressedBrush
        {
            get { return (Brush)GetValue(PressedBrushProperty); }
            set { SetValue(PressedBrushProperty, value); }
        }

        public ImageSource SmallImageSource
        {
            get { return (ImageSource)GetValue(SmallImageSourceProperty); }
            set { SetValue(SmallImageSourceProperty, value); }
        }
    }
}
